/*
 * Flashlib - QB9 common Flash library
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


package com.qb9.flashlib.display
{
	import com.qb9.flashlib.geom.Anchor;
	import com.qb9.flashlib.interfaces.IMoveable;
	import com.qb9.flashlib.math.QMath;
	
	import flash.display.DisplayObject;
	import flash.geom.Matrix;
	import flash.geom.Rectangle;
	
	// TODO: Consider rotation while calculating limits
	public class Camera2D implements IMoveable
	{
		protected var canvas:DisplayObject;

		protected var viewport:Rectangle;
		
		protected var anchor:Anchor;
		
		protected var _zoom:Number = 1;
		protected var _angle:Number = 0;
		protected var _x:Number = 0;
		protected var _y:Number = 0;
		
		protected var locked:Boolean = false;
		
		public function Camera2D(canvas:DisplayObject, viewport:Rectangle = null, anchor:Anchor = null)
		{
			this.canvas = canvas;
			this.anchor = anchor || Anchor.center;
			this.viewport = viewport || new Rectangle(0,0, canvas.stage.stageWidth, canvas.stage.stageHeight);
		}
		
		// Locking
		
		public function lock() : void
		{
			locked = true;
		}
		
		public function unlock() : void
		{
			locked = false;
			fullUpdate();
		}
		
		// Rotation
		
		public function get angle() : Number
		{
			return _angle;
		}
		
		public function set angle( v:Number ) : void
		{
			_angle = v;
			update();
		}
		
		protected function get radAngle() : Number
		{
			return QMath.deg2rad(angle);
		}
		
		// Internal utils
		
		protected function get viewportWidth() : Number
		{
			return viewport.width / zoom;
		}
		
		protected function get viewportHeight() : Number
		{
			return viewport.height / zoom;
		}
		
		protected function get rx() : Number
		{
			return viewportWidth * anchor.u;
		}
		
		protected function get ry() : Number
		{
			return viewportHeight * anchor.v;
		}
		
		// X & Y (center)
		
		public function get x() : Number
		{
			return _x;
		}
		
		public function set x(v:Number) : void
		{
			_x = v;
			limitX();
			update();
		}
		
		public function get y() : Number
		{
			return _y;
		}
		
		public function set y(v:Number) : void
		{
			_y = v;
			limitY();
			update();
		}
		
		// FIXME: Not sure this works correctly yet
		public function get bounds() : Rectangle
		{
			return new Rectangle(
				x - rx,
				y - ry,
				viewportWidth,
				viewportHeight
			);
		}
		
		// Zoom
		
		public function get zoom() : Number
		{
			return _zoom;
		}
		
		public function set zoom( v:Number ) : void
		{
			_zoom = v;
			fullUpdate();
		}
		
		// Calculate limits
		
		protected function limit(num:Number, all:Number, r:Number) : Number
		{
			return QMath.clamp(num, r, all / zoom - r);
		}
		
		protected function limitX() : void
		{
			// necessary
			canvas.scaleX = zoom;
			_x = limit(x, canvas.width, rx);
		}
		
		protected function limitY() : void
		{
			// necessary
			canvas.scaleY = zoom;
			_y = limit(y, canvas.height, ry);
		}
		
		// Update visually
		
		protected function fullUpdate() : void
		{
			limitX();
			limitY();
			update();
		}
		
		protected function update() : void
		{
			if (locked)
				return;
			
			var m:Matrix = new Matrix();
			
			m.translate(-x,-y);
			m.rotate(radAngle);
			m.scale(zoom, zoom);
			m.translate(rx * zoom, ry * zoom);

			canvas.transform.matrix = m;
		}
	}
}